home *** CD-ROM | disk | FTP | other *** search
- /*
- File: StorageClassBulkProtocol.c
-
- Contains: All code specific to handling the Bulk Only Protocol
-
- Version: 1.3
-
- Copyright: © 1998-1999 by Apple Computer, Inc., all rights reserved.
- */
- #include <DriverServices.h>
-
- #include "StorageClassBulkProtocol.h"
-
- // All definitions and structures for the Bulk Only Command Block Wrapper (CBW)
- #define kCommandBlockWrapperSignature 'USBC'
- #define kByteCountOfCBW 31
-
- struct StorageBulkOnlyCBW
- {
- UInt32 cbwSignature;
- UInt32 cbwTag;
- UInt32 cbwTransferLength;
- UInt8 cbwFlags;
- UInt8 cbwLUN; // Bits 0-3: LUN, 4-7: Reserved
- UInt8 cbwCDBLength; // Bits 0-4: CDB Length, 5-7: Reserved
- UInt8 cbwCDB[kUSBStorageMaxCDBSize];
- UInt8 cbwBulkPad[4];
- };
-
- typedef struct StorageBulkOnlyCBW StorageBulkOnlyCBW;
- typedef StorageBulkOnlyCBW *StorageBulkOnlyCBWPtr;
-
- enum
- {
- kCBWFlagsDataOut = 0x00,
- kCBWFlagsDataIn = 0x80
- };
-
- // All definitions and structures for the Bulk Only Command Status Wrapper (CSW)
- #define kCommandStatusWrapperSingature 'USBS'
- #define kByteCountOfCSW 13
-
- struct StorageBulkOnlyCSW
- {
- UInt32 cswSignature;
- UInt32 cswTag;
- UInt32 cswDataResidue;
- UInt8 cswStatus;
- };
-
- typedef struct StorageBulkOnlyCSW StorageBulkOnlyCSW;
- typedef StorageBulkOnlyCSW *StorageBulkOnlyCSWPtr;
-
- enum
- {
- kCSWCommandPassedError = 0x00, // No error occurred
- kCSWCommandFailedError = 0x01, // An error occurred ( probably a bad command or parameter )
- kCSWPhaseError = 0x02 // A transfer was performed in the wrong sequence
- };
-
- // All definitions and structures used by this module
- struct StorageClassBulkOnlyPB
- {
- USBPB usbPB;
- StorageBulkOnlyCBW cbw;
- StorageBulkOnlyCSW csw;
- UInt8 GetStatusBuffer[2]; // 2 bytes as specified in the USB spec
- Boolean busy;
- UInt32 flags; // Flags from StorageClassRequest pb
- UInt32 currentSGElement; // Scatter Gather element currently being transferred
- UInt32 currentSGCount; // Scatter Gather element currently being transferred
- StorageExecuteCommandPBPtr userPBPtr;
- };
- typedef struct StorageClassBulkOnlyPB StorageClassBulkOnlyPB;
- typedef StorageClassBulkOnlyPB *StorageClassBulkOnlyPBPtr;
-
- // Bulk Only State Machine States
- enum
- {
- kBulkOnlyCommandSent = 1,
- kBulkOnlyBulkIOComplete,
- kBulkOnlyCheckBulkStall,
- kBulkOnlyClearBulkStall,
- kBulkOnlyStatusReceived,
- kBulkOnlyStatusReceived2ndTime,
- kBulkOnlyResetCompleted,
- kBulkOnlyClearBulkInCompleted,
- kBulkOnlyClearBulkOutCompleted
- };
-
- static StorageClassBulkOnlyPB gBulkCommandPB; // Used only by StorageClassDriverExecuteCommand
- static UInt32 bulkOnlyTag = 1; // Count used to identify a Bulk Only command
-
- // Bulk Only function protocols
- static OSStatus SendCBWPacket(StorageExecuteCommandPBPtr cmdPBPtr, StorageBulkOnlyCBWPtr theCBW, USBPB* usbPB);
- static OSStatus ReceiveCSWPacket(StorageBulkOnlyCSWPtr theCSW, USBPB* usbPB, UInt32 nextState);
- static OSStatus BulkDeviceReset( USBPB* usbPB );
- static void BulkOnlyExecuteCommandCompletion(USBPB* usbPB);
-
- OSStatus StorageClassBulkOnlyAbortCommand( StorageExecuteCommandPBPtr cmdPBPtr )
- {
- OSStatus err = noErr;
-
- if (( gBulkCommandPB.busy == true ) && ( cmdPBPtr == gBulkCommandPB.userPBPtr ))
- {
- // If there is a command pending, and the command is the one we want to
- // abort, go ahead and do it.
- USBReference pipeRef;
-
- switch ( gBulkCommandPB.usbPB.usbRefcon )
- {
- case kBulkOnlyCommandSent:
- {
- pipeRef = GetBulkInPipeRef();
- }
- break;
-
- case kBulkOnlyBulkIOComplete:
- {
- if (gBulkCommandPB.flags & kStorageDataIn)
- {
- pipeRef = GetBulkInPipeRef();
- }
- else
- {
- pipeRef = GetBulkOutPipeRef();
- }
- }
- break;
-
- case kBulkOnlyStatusReceived:
- case kBulkOnlyStatusReceived2ndTime:
- {
- pipeRef = GetBulkInPipeRef();
- }
- break;
-
- default:
- {
- pipeRef = GetInterfaceRef();
- }
- break;
- }
-
- err = USBAbortPipeByReference(pipeRef);
- }
- else
- {
- err = abortErr;
- }
-
- return err;
- }
-
- // Prepare the Command Block Wrapper packet for Bulk Only Protocol
- OSStatus SendCBWPacket(StorageExecuteCommandPBPtr cmdPBPtr, StorageBulkOnlyCBWPtr theCBW, USBPB* usbPB)
- {
- theCBW->cbwSignature = kCommandBlockWrapperSignature;
- theCBW->cbwTag = bulkOnlyTag++;
- theCBW->cbwTransferLength = HostToUSBLong(cmdPBPtr->expectedCount);
- if (cmdPBPtr->flags & kStorageDataIn)
- {
- theCBW->cbwFlags = kCBWFlagsDataIn;
- }
- else if (cmdPBPtr->flags & kStorageDataOut)
- {
- theCBW->cbwFlags = kCBWFlagsDataOut;
- }
- else
- {
- theCBW->cbwFlags = 0;
- }
-
- theCBW->cbwLUN = 0; // Bits 0-3: LUN, 4-7: Reserved
- theCBW->cbwCDBLength = kUSBStorageMaxCDBSize; // Bits 0-4: CDB Length, 5-7: Reserved
- BlockCopy(&cmdPBPtr->cdb[0], theCBW->cbwCDB, kUSBStorageMaxCDBSize);
-
- InitParamBlock( GetBulkOutPipeRef(), usbPB);
-
- usbPB->usbReqCount = kByteCountOfCBW;
- usbPB->usbRefcon = kBulkOnlyCommandSent;
- usbPB->usbActCount = 0;
- usbPB->usbBuffer = theCBW;
- usbPB->usbCompletion = (USBCompletion)BulkOnlyExecuteCommandCompletion;
- usbPB->usbFlags |= kUSBTimeout; // If the device doesn't ack the CBW, allow a time out
- usbPB->usbFrame = 5000; // We want a 5 second timeout (5000 1ms USB Frames)
-
- return USBBulkWrite( usbPB );
- }
-
- // Prepare the Command Status Wrapper packet for Bulk Only Protocol
- OSStatus ReceiveCSWPacket(StorageBulkOnlyCSWPtr theCSW, USBPB* usbPB, UInt32 nextState)
- {
- InitParamBlock( GetBulkInPipeRef(), usbPB);
-
- usbPB->usbReqCount = kByteCountOfCSW;
- usbPB->usbRefcon = nextState;
- usbPB->usbActCount = 0;
- usbPB->usbBuffer = theCSW;
- usbPB->usbCompletion = (USBCompletion)BulkOnlyExecuteCommandCompletion;
-
- return USBBulkRead( usbPB );
- }
-
- OSStatus BulkDeviceReset( USBPB* usbPB )
- {
- InitParamBlock( GetInterfaceRef(), usbPB);
-
- usbPB->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBNone, kUSBClass, kUSBInterface);
- usbPB->usb.cntl.BRequest = 0xFF;
- usbPB->usb.cntl.WValue = 0;
- usbPB->usb.cntl.WIndex = 0;
-
- usbPB->usbFlags = kUSBNo5SecTimeout | kUSBAddressRequest;
-
- usbPB->usbReqCount = 0;
- usbPB->usbRefcon = kBulkOnlyResetCompleted;
- usbPB->usbActCount = 0;
- usbPB->usbBuffer = nil;
- usbPB->usbCompletion = (USBCompletion)BulkOnlyExecuteCommandCompletion;
-
- return USBDeviceRequest(usbPB);
- }
-
- // All Bulk Only protocol device requests come through here
- OSStatus StorageClassBulkOnlyExecuteCommand( StorageExecuteCommandPBPtr cmdPBPtr )
- {
- OSStatus myErr;
-
- // check if we already have a read in progress, if so return error.
- if (gBulkCommandPB.busy == true)
- {
- cmdPBPtr->status = kCommandBusyError;
- return kCommandBusyError;
- }
-
- BlockZero(&gBulkCommandPB, sizeof(StorageClassBulkOnlyPB));
-
- gBulkCommandPB.busy = true;
-
- // Get a local copy of the callers cdb
- BlockCopy(&cmdPBPtr->cdb[0], &gBulkCommandPB.cbw.cbwCDB[0], kUSBStorageMaxCDBSize);
-
- gBulkCommandPB.flags = cmdPBPtr->flags;
-
- gBulkCommandPB.userPBPtr = (StorageExecuteCommandPBPtr) cmdPBPtr; // Save the ptr to the callers PB
-
- // Clear out the sutosense is valid field, currently not used by Bulk Only protocol
- cmdPBPtr->autoStatusIsValid = false;
-
- myErr = SendCBWPacket(cmdPBPtr, &gBulkCommandPB.cbw, &gBulkCommandPB.usbPB);
- if (myErr != kRequestPending )
- {
- // The command completed immediately ( we probably got an error )
- // clear the PB's busy flag and return
- gBulkCommandPB.busy = false;
- }
-
- cmdPBPtr->status = myErr;
- return myErr;
- }
-
- void BulkOnlyExecuteCommandCompletion(USBPB* usbPB)
- {
- StorageExecuteCommandPBPtr cmdPBPtr;
- StorageClassBulkOnlyPBPtr bulkPBPtr;
-
- // Get the command's Bulk PB
- bulkPBPtr = (StorageClassBulkOnlyPBPtr) usbPB;
-
- // Retrieve the callers pb
- cmdPBPtr = (StorageExecuteCommandPBPtr) bulkPBPtr->userPBPtr;
-
- switch(usbPB->usbRefcon)
- {
- case kBulkOnlyCommandSent:
- {
- // If there is to be no data transfer then we are done and can return to the caller
- if (bulkPBPtr->flags & kStorageNoData)
- {
- if (usbPB->usbStatus != noErr)
- {
- cmdPBPtr->status = usbPB->usbStatus;
- }
- else
- {
- // Bulk transfer is done, get the Command Status Wrapper from the device
- cmdPBPtr->status = ReceiveCSWPacket(&bulkPBPtr->csw, usbPB, kBulkOnlyStatusReceived);
- }
-
- // There is no data to be transfered
- break;
- }
-
- if (usbPB->usbStatus != noErr)
- {
- // An error occurred, probably a timeout error,
- // and the command was not successfully sent to the device.
- cmdPBPtr->status = usbPB->usbStatus;
- break;
- }
-
- // Setup the usb pb for either bulk in or out
- if (bulkPBPtr->flags & kStorageDataIn)
- {
- InitParamBlock( GetBulkInPipeRef(), usbPB);
- }
- else if (bulkPBPtr->flags & kStorageDataOut)
- {
- InitParamBlock( GetBulkOutPipeRef(), usbPB);
- }
-
- if (bulkPBPtr->flags & kStorageSGBuffer)
- {
- // We have a scatter-gather transfer
- USBSGListPtr theSGList;
- USBSGElementPtr currentSGElement;
-
- bulkPBPtr->currentSGElement = 0;
- bulkPBPtr->currentSGCount = 0;
- theSGList = (USBSGListPtr) cmdPBPtr->userBuffer;
- currentSGElement = &theSGList->sgElementList[0];
- if ( currentSGElement->SGCount > kUSBMaxBulkTransfer )
- {
- usbPB->usbReqCount = kUSBMaxBulkTransfer;
- }
- else
- {
- usbPB->usbReqCount = currentSGElement->SGCount;
- }
-
- usbPB->usbBuffer = currentSGElement->SGAddr;
- }
- else
- {
- // We have a single buffer transfer
- if ( cmdPBPtr->expectedCount > kUSBMaxBulkTransfer )
- {
- usbPB->usbReqCount = kUSBMaxBulkTransfer;
- }
- else
- {
- usbPB->usbReqCount = cmdPBPtr->expectedCount;
- }
-
- usbPB->usbBuffer = cmdPBPtr->userBuffer;
- }
-
- usbPB->usbRefcon = kBulkOnlyBulkIOComplete;
- usbPB->usbActCount = 0;
- usbPB->usbCompletion = (USBCompletion)BulkOnlyExecuteCommandCompletion;
-
- // Start a bulk in or out transaction
- if (bulkPBPtr->flags & kStorageDataIn)
- {
- cmdPBPtr->status = USBBulkRead(&bulkPBPtr->usbPB);
- }
- else if (bulkPBPtr->flags & kStorageDataOut)
- {
- cmdPBPtr->status = USBBulkWrite(&bulkPBPtr->usbPB);
- }
- }
- break;
-
- case kBulkOnlyBulkIOComplete:
- {
- cmdPBPtr->actualCount += usbPB->usbActCount; // Update the users byte count
- cmdPBPtr->status = usbPB->usbStatus; // and status
-
- if (usbPB->usbStatus == noErr)
- {
- if (( cmdPBPtr->actualCount != cmdPBPtr->expectedCount) && ( usbPB->usbActCount == usbPB->usbReqCount ))
- {
- // If we have not yet transfered all the data and there are no Errors and we did not get a short packet.
- // Setup the usb pb for either bulk in or out
- UInt32 actCount = usbPB->usbActCount;
-
- if (bulkPBPtr->flags & kStorageDataIn)
- {
- InitParamBlock( GetBulkInPipeRef(), usbPB);
- }
- else if (bulkPBPtr->flags & kStorageDataOut)
- {
- InitParamBlock( GetBulkOutPipeRef(), usbPB);
- }
-
- if (bulkPBPtr->flags & kStorageSGBuffer)
- {
- // We have a scatter-gather transfer
- USBSGListPtr theSGList;
- USBSGElementPtr currentSGElement;
-
- bulkPBPtr->currentSGCount += actCount;
- theSGList = (USBSGListPtr) cmdPBPtr->userBuffer;
- currentSGElement = &theSGList->sgElementList[bulkPBPtr->currentSGElement];
-
- if ( bulkPBPtr->currentSGCount >= currentSGElement->SGCount)
- {
- // Move on to next SG segment
- bulkPBPtr->currentSGElement++;
- currentSGElement = &theSGList->sgElementList[bulkPBPtr->currentSGElement];
- bulkPBPtr->currentSGCount = 0;
- if ( currentSGElement->SGCount > kUSBMaxBulkTransfer )
- {
- usbPB->usbReqCount = kUSBMaxBulkTransfer;
- }
- else
- {
- usbPB->usbReqCount = currentSGElement->SGCount;
- }
-
- usbPB->usbBuffer = currentSGElement->SGAddr;
- }
- else
- {
- if ( (currentSGElement->SGCount - bulkPBPtr->currentSGCount) > kUSBMaxBulkTransfer )
- {
- usbPB->usbReqCount = kUSBMaxBulkTransfer;
- }
- else
- {
- usbPB->usbReqCount = (currentSGElement->SGCount - bulkPBPtr->currentSGCount);
- }
-
- usbPB->usbBuffer = currentSGElement->SGAddr + bulkPBPtr->currentSGCount;
- }
- }
- else
- {
- if ( (cmdPBPtr->expectedCount - cmdPBPtr->actualCount) > kUSBMaxBulkTransfer )
- {
- usbPB->usbReqCount = kUSBMaxBulkTransfer;
- }
- else
- {
- usbPB->usbReqCount = (cmdPBPtr->expectedCount - cmdPBPtr->actualCount);
- }
-
- usbPB->usbBuffer = (cmdPBPtr->userBuffer) + (cmdPBPtr->actualCount);
- }
-
- usbPB->usbRefcon = kBulkOnlyBulkIOComplete;
- usbPB->usbActCount = 0;
- usbPB->usbCompletion = (USBCompletion)BulkOnlyExecuteCommandCompletion;
-
- // Continue a bulk in or out transaction
- if (bulkPBPtr->flags & kStorageDataIn)
- {
- cmdPBPtr->status = USBBulkRead(&bulkPBPtr->usbPB);
- }
- else if (bulkPBPtr->flags & kStorageDataOut)
- {
- cmdPBPtr->status = USBBulkWrite(&bulkPBPtr->usbPB);
- }
- }
- else
- {
- // Bulk transfer is done, get the Command Status Wrapper from the device
- cmdPBPtr->status = ReceiveCSWPacket(&bulkPBPtr->csw, usbPB, kBulkOnlyStatusReceived);
- }
- }
- else
- {
- // Either an error occurred on transfer or we did not get all the data we requested.
- // In either case, this transfer is complete, clean up and return an error to the client.
- // Check if the bulk endpoint was stalled
- USBPipeRef pipeRef = 0;
-
- if (bulkPBPtr->flags & kStorageDataIn)
- {
- pipeRef = GetBulkInPipeRef();
- }
- else if (bulkPBPtr->flags & kStorageDataOut)
- {
- pipeRef = GetBulkOutPipeRef();
- }
- else
- {
- pipeRef = GetInterfaceRef();
- }
-
- cmdPBPtr->status = GetStatusEndpointStatus( usbPB, pipeRef, (USBCompletion) BulkOnlyExecuteCommandCompletion,(Ptr) bulkPBPtr->GetStatusBuffer, kBulkOnlyCheckBulkStall);
- }
- }
- break;
-
- case kBulkOnlyCheckBulkStall:
- {
- // Check to see if the endpoint was stalled
- if ( (bulkPBPtr->GetStatusBuffer[0] & 1) == 1 )
- {
- USBPipeRef pipeRef = 0;
-
- if (bulkPBPtr->flags & kStorageDataIn)
- {
- pipeRef = GetBulkInPipeRef();
- }
- else if (bulkPBPtr->flags & kStorageDataOut)
- {
- pipeRef = GetBulkOutPipeRef();
- }
- else
- {
- pipeRef = GetInterfaceRef();
- }
-
- cmdPBPtr->status = ClearFeatureEndpointStall( usbPB, pipeRef, (USBCompletion) BulkOnlyExecuteCommandCompletion, kBulkOnlyClearBulkStall);
- }
- else
- {
- // If the endpoint was not stalled, attempt to get the CSW
- cmdPBPtr->status = ReceiveCSWPacket(&bulkPBPtr->csw, usbPB, kBulkOnlyStatusReceived);
- }
- }
- break;
-
- case kBulkOnlyClearBulkStall:
- {
- // The pipe was stalled and an attempt to clear it was made
- // Try to get the CSW. If the pipe was not successfully cleared, this will also
- // set off a device reset sequence
- cmdPBPtr->status = ReceiveCSWPacket(&bulkPBPtr->csw, usbPB, kBulkOnlyStatusReceived);
- }
- break;
-
- case kBulkOnlyStatusReceived:
- {
- // Bulk transfer is done, get the Command Status Wrapper from the device
- if (usbPB->usbStatus != noErr)
- {
- // An error occurred trying to get the first CSW, we should check and clear the stall,
- // and then try the CSW again
- cmdPBPtr->status = ReceiveCSWPacket(&bulkPBPtr->csw, usbPB, kBulkOnlyStatusReceived2ndTime);
- }
- else
- {
- // Process the CSW and determine appropriate response
- if (( bulkPBPtr->csw.cswTag == bulkPBPtr->cbw.cbwTag) && ( bulkPBPtr->csw.cswStatus == kCSWCommandPassedError ))
- {
- // The device reports no error on the command, and the command has
- // the same tag as the command that was sent, check to make sure all data was retrieved
- if (cmdPBPtr->actualCount == cmdPBPtr->expectedCount )
- {
- // We were able to get all the data for the device
- cmdPBPtr->status = noErr;
- }
- else
- {
- // An error occurred and we did not get all the data
- cmdPBPtr->status = kUSBPipeStalledError;
- }
- }
- else
- {
- // The device reported an error on the command
- // report an error to the client
- cmdPBPtr->status = kUSBPipeStalledError;
- }
- }
- }
- break;
-
- case kBulkOnlyStatusReceived2ndTime:
- {
- // Second try for the CSW is done, if an error occurred, reset device.
- if (usbPB->usbStatus != noErr)
- {
- cmdPBPtr->status = BulkDeviceReset(usbPB);
- }
- else
- {
- cmdPBPtr->status = kUSBInternalErr;
- }
- }
- break;
-
- case kBulkOnlyResetCompleted:
- {
- cmdPBPtr->status = ClearFeatureEndpointStall( usbPB, GetBulkInPipeRef(), (USBCompletion) BulkOnlyExecuteCommandCompletion, kBulkOnlyClearBulkInCompleted);
- }
- break;
-
- case kBulkOnlyClearBulkInCompleted:
- {
- cmdPBPtr->status = ClearFeatureEndpointStall( usbPB, GetBulkOutPipeRef(), (USBCompletion) BulkOnlyExecuteCommandCompletion, kBulkOnlyClearBulkOutCompleted);
- }
- break;
-
- case kBulkOnlyClearBulkOutCompleted:
- {
- cmdPBPtr->actualCount = 0;
- cmdPBPtr->status = kUSBInternalErr;
- }
- break;
-
- default:
- {
- cmdPBPtr->actualCount = 0;
- cmdPBPtr->status = kUSBInternalErr;
- }
- break;
- }
-
- if ( cmdPBPtr->status != kRequestPending )
- {
- bulkPBPtr->busy = false;
- if(cmdPBPtr->completionProc != nil)
- {
- (*cmdPBPtr->completionProc)((StorageExecuteCommandPBPtr) cmdPBPtr);
- }
- }
- }
-
-
-
-